#include "stdafx.h"

#include ".\GDIUtils.h"

#include ".\SelectedPolygon.h"
#include "Polygon.h"

extern		COLORREF			g_Colors[DHC_SIZE];
extern		unsigned int		g_Sizes[DHC_SIZE];

CSelectedPolygon::CSelectedPolygon(void)
{
	m_pMultiPolygon = NULL;

	m_bGrouped = false;
	Clear();
}

CSelectedPolygon::~CSelectedPolygon(void)
{
}

void		CSelectedPolygon::SetMultiPolygon( CMultiPolygon *pMP )
{
	m_pMultiPolygon = pMP;
}

void		CSelectedPolygon::Clear()
{
	m_arSelectedIdx.clear();
	//m_bGrouped = false;
	m_nCurSelectIdx = -1;
}

int			CSelectedPolygon::Size()
{
	return (int)m_arSelectedIdx.size();
}

int			CSelectedPolygon::GetAt( int idx )
{
	assert( idx>=0 && idx<Size() );
	return m_arSelectedIdx[idx];
}

void		CSelectedPolygon::Add( int poly )
{
	// sorting by increasing sequence before adding it
	int idx = 0;
	while( idx < Size() )
	{
		if( poly < GetAt(idx) )
			break;

		idx++;
	}
	m_arSelectedIdx.insert( m_arSelectedIdx.begin()+idx, poly );
	//m_arSelectedIdx.push_back( poly );
}

void		CSelectedPolygon::RemoveAt( int idx )
{
	m_arSelectedIdx.erase( m_arSelectedIdx.begin() + idx );
}


// order operations
void		CSelectedPolygon::BringToFront()
{
	int num = Size();
	if( num == 0 )
		return ;

	for( int i=0; i<num; i++ )
	{
		int polyidx = GetAt(i);
		int offset = i - polyidx;
		m_pMultiPolygon->ChangeOrder( polyidx, offset );
		m_arSelectedIdx[ i ] = i;
	}
}

void		CSelectedPolygon::SendToBack()
{
	int num = Size();
	if( num == 0 )
		return ;

	int nTotal = m_pMultiPolygon->NumPolygon();
	for( int i=num-1; i>=0; i-- )
	{
		int polyidx = GetAt(i);
		int offset = nTotal-num+i - polyidx;
		m_pMultiPolygon->ChangeOrder( polyidx, offset );
		m_arSelectedIdx[ i ] = nTotal-num+i;
	}
}

void		CSelectedPolygon::BringForward()
{
	int num = Size();
	if( num == 0 )
		return ;

	// find the first move
	int idx = 0;
	while( idx<num )
	{
		if( GetAt(idx) == idx )
			idx++ ;
		else
			break;
	}

	for( int i=idx; i<num; i++ )
	{
		int polyidx = GetAt(i);
		int offset = -1;
		m_pMultiPolygon->ChangeOrder( polyidx, offset );
		m_arSelectedIdx[ i ] = polyidx + offset;
	}
}

void		CSelectedPolygon::SendBackward()
{
	int num = Size();
	if( num == 0 )
		return ;

	// find the first move
	int idx = num-1;
	int nTotal = m_pMultiPolygon->NumPolygon();
	while( idx>=0 )
	{
		if( GetAt(idx) == nTotal-num+idx )
			idx-- ;
		else
			break;
	}

	for( int i=idx; i>=0; i-- )
	{
		int polyidx = GetAt(i);
		int offset = 1;
		m_pMultiPolygon->ChangeOrder( polyidx, offset );
		m_arSelectedIdx[ i ] = polyidx + offset;
	}
}


int			CSelectedPolygon::IsSelected( int poly )
{
	for( int i=0; i<Size(); i++ )
	{
		if( GetAt(i) == poly )
			return i;
	}

	return -1;
}

void		CSelectedPolygon::SetFilled( bool filled )
{
	for( int i=0; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		pPoly->SetFilled( filled );
	}
}

void		CSelectedPolygon::SetFilledColor( COLORREF color )
{
	for( int i=0; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		pPoly->SetFilledColor( color );
	}
}

void		CSelectedPolygon::Connect()
{
	assert( Size() == 2 );
	m_pMultiPolygon->Connect( GetAt(0), GetAt(1) );
	m_arSelectedIdx.erase( m_arSelectedIdx.begin() + 1 );	// delete
}

HandleMode	CSelectedPolygon::PointHandleMode( double px, double py )
{
	if( m_bGrouped )
	{
		for( int mode=POLYHNDL_UPPERLEFT; mode<POLYHNDL_NUMBER; mode++ )
		{
			CVertex &vert = m_Handles[ mode ];
			if( CGDIUtils::PointInPoint( px, py, vert.x, vert.y  ) )
			{
				return (HandleMode)mode;
			}
		}

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon(i);
			if( pPoly->PolyContainPoint( px, py ) )
				return POLYHNDL_INSIDE;
		}

		return POLYHNDL_OUTSIDE;
	}
	else
	{
		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon(i);
			HandleMode mode = pPoly->PointHandleMode( px, py ) ;
			if( mode != POLYHNDL_OUTSIDE )
			{
				m_nCurSelectIdx = i;	// record the current selected polyon
				return mode;
			}
		}

		return POLYHNDL_OUTSIDE;
	}
}


int			CSelectedPolygon::FindFirstPolyContainPoint( double px, double py, int flag )
{
	int poly = m_pMultiPolygon->FindFirstPolyContainPoint( px, py );

	// not find any polygon contain the cursor
	if( poly<0 )
	{
		if( !flag )	// no CTRL pressed: select none
			Clear();

		return FALSE;
	}

	// find poly contain the cursor
	if( !flag )	// no CTRL pressed: select only this polygon
	{
		Clear();
		Add( poly );
	}
	else	// CTRL pressed: toggle on/off the polygon selected
	{
		int idx = IsSelected( poly );

		if( idx>-1 )	// if it is selected before
		{
			RemoveAt( idx );
		}
		else
		{
			Add( poly );
		}
	}

	ComputeHandles();
	SaveOriginSize();
	//UpdateChanges();

	return TRUE;
}

// subdivide all edges for each selected polygon
void		CSelectedPolygon::SubAllEdge(int nSeg)
{
	for(int i=0; i<Size(); i++ )
	{
		CPolygon *pPoly = this->GetPolygon(i);
		pPoly->SubAllEdge( nSeg );
	}
}


//int			CSelectedPolygon::MaxIndex()
//{
//	int nMax = -1;
//	int maxidx = 0;
//	for( int i=0; i<Size();i++ )
//	{
//		int idx = m_arSelectedIdx[i];
//		if( idx > nMax )
//		{
//			nMax = idx;
//			maxidx = i ;
//		}
//	}
//	return maxidx;
//}

void		CSelectedPolygon::RemoveSelectedPolygons()
{
	// sort the index list so that we remove the polygon from back to front
	// a simple method
	//while( Size()>0)
	//{
	//	int i = MaxIndex();
	//	int idx = m_arSelectedIdx[ i ];
	//	m_pMultiPolygon->RemovePolygon( idx );
	//	m_arSelectedIdx.erase( m_arSelectedIdx.begin() + i );
	//}

	// make sure the list is sorted increasingly
	for( int i=Size()-1; i>=0; i-- )
	{
		int idx = m_arSelectedIdx[ i ];
		m_pMultiPolygon->RemovePolygon( idx );
	}

	Clear();
}


// clone the selected polygons at the original position
void		CSelectedPolygon::Clone()	
{
	int num = Size();
	if( num == 0 )
		return ;

	int maxidx = GetAt(num-1);
	for( int i=0; i<num; i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		CPolygon *pNewPoly = pPoly->Clone();
		//m_pMultiPolygon->AddPolygon( pNewPoly, true );
		m_pMultiPolygon->InsertPolygonAt( maxidx+1, pNewPoly );
		maxidx++;
	}
	SaveOriginSize();
}

void		CSelectedPolygon::ZoomHandles( double ratio )
{
	// compute the handles
	for( int i=0; i<POLYHNDL_NUMBER; i++ )
	{
		CVertex &pV = m_Handles[i];
		pV.Set( pV.x * ratio, pV.y * ratio );
	}
}


void		CSelectedPolygon::SetGrouped( BOOL group )	
{ 
	m_bGrouped = group; 
	m_nCurSelectIdx = -1; 

	ComputeHandles();
}


CPolygon	*CSelectedPolygon::GetPolygon( int idx )
{
	return m_pMultiPolygon->GetPolygon( GetAt(idx) );
}


void		CSelectedPolygon::UpdateChanges()
{
	ComputeBoundingBox();
}

void		CSelectedPolygon::ComputeHandles()
{
	ComputeBoundingBox();
	ComputeAnchor();
}

// compute anchor point
void		CSelectedPolygon::ComputeAnchor()
{
	m_Handles[ POLYHNDL_ANCHOR    ].Set( (XMin()+XMax())/2, (YMin()+YMax())/2 );
}

// compute bounding box
void		CSelectedPolygon::ComputeBoundingBox()
{
	// the bounding box
	if( Size() == 0 )
		return ;

	CPolygon *pPoly = GetPolygon( 0 );
	CRect bndrect( (int)pPoly->XMin(), (int)pPoly->YMin(), (int)pPoly->XMax(), (int)pPoly->YMax() );

	for( int i=1; i<Size(); i++ )
	{
		pPoly = GetPolygon( i );
		CRect bnd( (int)pPoly->XMin(), (int)pPoly->YMin(), (int)pPoly->XMax(), (int)pPoly->YMax() );
		//bndrect.UnionRect( &bndrect, &bnd );	// not correct for "point" polygon
		bndrect = CGDIUtils::UnionRect( bndrect, bnd );
	}

	double xmin = bndrect.left, ymin = bndrect.top, xmax = bndrect.right, ymax = bndrect.bottom ;
	double xcen = (xmin+xmax)/2, ycen=(ymin+ymax)/2 ;

	// set all the handles
	m_Handles[ POLYHNDL_UPPERLEFT ].Set( xmin, ymin );
	m_Handles[ POLYHNDL_LEFT      ].Set( xmin, ycen );
	m_Handles[ POLYHNDL_LOWERLEFT ].Set( xmin, ymax );
	m_Handles[ POLYHNDL_BOTTOM    ].Set( xcen, ymax );
	m_Handles[ POLYHNDL_LOWERRIGHT].Set( xmax, ymax );
	m_Handles[ POLYHNDL_RIGHT     ].Set( xmax, ycen );
	m_Handles[ POLYHNDL_UPPERRIGHT].Set( xmax, ymin );
	m_Handles[ POLYHNDL_UPPER     ].Set( xcen, ymin );
}

// resample the polygons
void CSelectedPolygon::SaveOriginSize()
{
	m_nOringVNs.clear();
	int sz = Size();	m_nOringVNs.resize(sz);
	for (int i=0; i<sz; ++i){
		CPolygon *pPoly = GetPolygon( i );
		m_nOringVNs[i] = pPoly->NumVertex();
	}
}

void	CSelectedPolygon::Resample( int nNumVertices, BOOL bKeepOrgVertices )
{
	for( int i=0; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon( i );
		pPoly->Resample( nNumVertices, bKeepOrgVertices );
	}
	UpdateChanges();
}

int CSelectedPolygon::ResampleByRatio( float rt )
{	
	int _TotalVN(0);
	for( int i=0; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon( i );
		int nNumVertices = rt * m_nOringVNs[i];
		if(nNumVertices<=1) {
			_TotalVN += pPoly->NumVertex();
			continue;
		}
		pPoly->Resample( nNumVertices, false );
		_TotalVN += pPoly->NumVertex();
	}
	UpdateChanges();
	return _TotalVN;
}

void	CSelectedPolygon::SetByRubberRegion( CPoint pt1, CPoint pt2 )
{
	Clear();

	int xmin = std::min( pt1.x, pt2.x );
	int xmax = std::max( pt1.x, pt2.x );
	int ymin = std::min( pt1.y, pt2.y );
	int ymax = std::max( pt1.y, pt2.y );
	CRect rubber( xmin, ymin, xmax, ymax );

	for( int i=0; i<m_pMultiPolygon->NumPolygon(); i++ )
	{
		CPolygon *pPoly = m_pMultiPolygon->GetPolygon(i);
		CRect bnd( (int)pPoly->XMin(), (int)pPoly->YMin(), (int)pPoly->XMax(), (int)pPoly->YMax() );
		
		//CRect temp;
		//temp.UnionRect( &rubber, &bnd );
		//if( temp == rubber )	// not correct for "point" polygon
		if( CGDIUtils::RectInRect(bnd, rubber) )
			Add( i );
	}
	ComputeHandles();
	SaveOriginSize();
}

void		CSelectedPolygon::SelectAll()
{
	Clear();
	
	for( int i=0; i<m_pMultiPolygon->NumPolygon(); i++ )
	{
		Add( i );
	}
	SaveOriginSize();
	ComputeHandles();
}


void		CSelectedPolygon::Draw( CDC *pDC, bool bDrawVertex )
{
	DrawHandles( pDC );
}

void		CSelectedPolygon::DrawBoundingBox( CDC *pDC )
{
	CPen pen( PS_SOLID, g_Sizes[DHC_BOUNDINGBOX], g_Colors[DHC_BOUNDINGBOX] );
	CGDISelectObject<CPen> tmpPen(pDC, &pen);

	int xmin = (int)(XMin()), ymin = (int)(YMin()), xmax = (int)(XMax()), ymax = (int)(YMax());
	pDC->MoveTo( xmin, ymin );
	pDC->LineTo( xmin, ymax );
	pDC->LineTo( xmax, ymax );
	pDC->LineTo( xmax, ymin );
	pDC->LineTo( xmin, ymin );
}

void		CSelectedPolygon::DrawHandles( CDC *pDC )
{
	// if not grouped , draw handles of all polygons
	if( !m_bGrouped )
	{
		for( int i=0; i<Size(); i++ )
		{
			GetPolygon(i)->DrawHandles( pDC );
		}
	}
	else
	{
		// draw bounding box
		DrawBoundingBox( pDC );

		// draw anchor
		CVertex &vert = m_Handles[ POLYHNDL_ANCHOR ];
		CGDIUtils::DrawFilledCircle( pDC, CPoint( (int)vert.x, (int)vert.y ), g_Sizes[DHC_ANCHOR], g_Colors[DHC_ANCHOR] );

		// draw other handles
		for( int i=0; i<POLYHNDL_NUMBER; i++ )
		{
			if( i==POLYHNDL_ANCHOR )
				continue ;

			CVertex &vert = m_Handles[ i ];
			CGDIUtils::DrawFilledBox( pDC, CPoint( (int)vert.x, (int)vert.y ), g_Sizes[DHC_HANDLES], g_Colors[DHC_HANDLES] );
		}
	}
}

CVertex		CSelectedPolygon::GetGroupHandle( HandleMode mode )
{
	assert( mode >=0 && mode<POLYHNDL_NUMBER );
	return m_Handles[mode];
}

CVertex		CSelectedPolygon::GetHandle( HandleMode mode )
{
	assert( mode >=0 && mode<POLYHNDL_NUMBER );

	if( m_bGrouped )
	{
		assert( m_nCurSelectIdx == -1 );
		return m_Handles[mode];
	}
	else
	{
		//assert( m_nCurSelectIdx>-1 && m_nCurSelectIdx<Size() );
		if( !(m_nCurSelectIdx>-1 && m_nCurSelectIdx<Size()) )
		{
			m_nCurSelectIdx = 0;
		}
		CPolygon *pPoly = GetPolygon( m_nCurSelectIdx );
		return pPoly->GetHandle( mode );
	}
}

// get the anchor position for showing
CVertex		CSelectedPolygon::GetAnchor()
{
	assert( Size()>0 );

	if( m_bGrouped )
	{
		return m_Handles[POLYHNDL_ANCHOR];
	}
	else
	{
		// get the first polygon
		CPolygon *pPoly = GetPolygon( 0 );
		return pPoly->GetHandle( POLYHNDL_ANCHOR );
	}
}

//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------
// translate
void		CSelectedPolygon::Translate( double px, double py )
{
	// both the grouped or ungrouped are the same
	// if( m_bGrouped )
	for( int i=0; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon( i );
		pPoly->Translate( px, py );
	}

	m_Handles[ POLYHNDL_ANCHOR ].x += px;
	m_Handles[ POLYHNDL_ANCHOR ].y += py;

	UpdateChanges();
}

void		CSelectedPolygon::TranslateAnchor( double px, double py )
{
	if( m_bGrouped )
	{
		m_Handles[ POLYHNDL_ANCHOR ].x += px;
		m_Handles[ POLYHNDL_ANCHOR ].y += py;
	}
	else
	{
		assert( m_nCurSelectIdx>-1 && m_nCurSelectIdx<Size() );
		CPolygon *pPoly = GetPolygon( m_nCurSelectIdx );
		pPoly->TranslateAnchor( px, py );
	}
}


//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------
// scale X
double		CSelectedPolygon::GetScaleXScale( double x0, double x1 )
{
	double xorg = m_Handles[ POLYHNDL_ANCHOR ].x ;
	double scale;
	if( fabs( x0 - xorg ) < MINMOVE )
		scale = 1.0;
	else
		scale = ( x1 - xorg ) / ( x0 - xorg );
	
	return scale;
}

void		CSelectedPolygon::ScaleMoveX( double x0, double x1 )
{
	if( m_bGrouped )
	{
		double scale = GetScaleXScale( x0, x1 );

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->ScaleX( scale, m_Handles[ POLYHNDL_ANCHOR ].x );
		}
	}
	else
	{
		assert( m_nCurSelectIdx>=0 && m_nCurSelectIdx<Size() );
		CPolygon *pSelPoly = GetPolygon( m_nCurSelectIdx );
		double scale = pSelPoly->GetScaleXScale(x0, x1);

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->ScaleX( scale );
		}
	}

	UpdateChanges();
}


//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------
// scale Y
double		CSelectedPolygon::GetScaleYScale( double y0, double y1 )
{
	double yorg = m_Handles[ POLYHNDL_ANCHOR ].y ;
	double scale;
	if( fabs( y0 - yorg ) < MINMOVE )
		scale = 1.0;
	else
		scale = ( y1 - yorg ) / ( y0 - yorg );

	return scale;
}


void		CSelectedPolygon::ScaleMoveY( double y0, double y1 )
{
	if( m_bGrouped )
	{
		double scale = GetScaleYScale( y0, y1 );

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->ScaleY( scale, m_Handles[ POLYHNDL_ANCHOR ].y );
		}
	}
	else
	{
		assert( m_nCurSelectIdx>=0 && m_nCurSelectIdx<Size() );
		CPolygon *pSelPoly = GetPolygon( m_nCurSelectIdx );
		double scale = pSelPoly->GetScaleYScale(y0, y1);

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->ScaleY( scale );
		}
	}

	UpdateChanges();
}

//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------
// scale X and Y
void		CSelectedPolygon::Scale( double scaleX, double scaleY )
{
	if( m_bGrouped )
	{
		double xorg = m_Handles[ POLYHNDL_ANCHOR ].x ;
		double yorg = m_Handles[ POLYHNDL_ANCHOR ].y ;

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->Scale( scaleX, scaleY, xorg, yorg );
		}
	}
	else
	{
		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->Scale( scaleX, scaleY );
		}
	}

	UpdateChanges();
}

void		CSelectedPolygon::ScaleMove( double x0, double y0, double x1, double y1 )
{
	if( m_bGrouped )
	{
		double scaleX = GetScaleXScale( x0, x1 );
		double scaleY = GetScaleYScale( y0, y1 );

		double xorg = m_Handles[ POLYHNDL_ANCHOR ].x ;
		double yorg = m_Handles[ POLYHNDL_ANCHOR ].y ;

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->Scale( scaleX, scaleY, xorg, yorg );
		}
	}
	else
	{
		assert( m_nCurSelectIdx>=0 && m_nCurSelectIdx<Size() );
		CPolygon *pSelPoly = GetPolygon( m_nCurSelectIdx );
		double scaleX = pSelPoly->GetScaleXScale(x0, x1);
		double scaleY = pSelPoly->GetScaleYScale(y0, y1);

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->Scale( scaleX, scaleY );
		}
	}

	UpdateChanges();
}

void		CSelectedPolygon::ScaleMoveUnified( double x0, double y0, double x1, double y1 )
{
	if( m_bGrouped )
	{
		double scaleX = GetScaleXScale( x0, x1 );
		double scaleY = GetScaleYScale( y0, y1 );
		double scale = ( scaleX < scaleY ) ? scaleX : scaleY;

		double xorg = m_Handles[ POLYHNDL_ANCHOR ].x ;
		double yorg = m_Handles[ POLYHNDL_ANCHOR ].y ;

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->Scale( scale, scale, xorg, yorg );
		}
	}
	else
	{
		assert( m_nCurSelectIdx>=0 && m_nCurSelectIdx<Size() );
		CPolygon *pSelPoly = GetPolygon( m_nCurSelectIdx );
		double scaleX = pSelPoly->GetScaleXScale(x0, x1);
		double scaleY = pSelPoly->GetScaleYScale(y0, y1);
		double scale = ( scaleX < scaleY ) ? scaleX : scaleY;

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->Scale( scale, scale );
		}
	}

	UpdateChanges();
}


//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------
// shear X
double		CSelectedPolygon::GetShearXScale( double x0, double y0, double x1, double y1 )
{
	double yorg = m_Handles[ POLYHNDL_ANCHOR ].y;

	double scale;
	if( fabs( y0 - yorg ) < MINMOVE )
		scale = 0;
	else
		scale = (x1 - x0)/(y0 - yorg );

	return scale;
}

void		CSelectedPolygon::ShearMoveX( double x0, double y0, double x1, double y1 )
{
	if( m_bGrouped )
	{
		double scale = GetShearXScale( x0, y0, x1, y1 );

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->ShearX( scale, m_Handles[ POLYHNDL_ANCHOR ].y );
		}
	}
	else
	{
		assert( m_nCurSelectIdx>=0 && m_nCurSelectIdx<Size() );
		CPolygon *pSelPoly = GetPolygon( m_nCurSelectIdx );
		double scale = pSelPoly->GetShearXScale(x0,y0,x1,y1);

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->ShearX( scale );
		}
	}

	UpdateChanges();
}


//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------
// shear Y
double		CSelectedPolygon::GetShearYScale( double x0, double y0, double x1, double y1 )
{
	double xorg = m_Handles[ POLYHNDL_ANCHOR ].x;

	double scale;
	if( fabs( x0 - xorg ) < MINMOVE )
		scale = 0;
	else
		scale = (y1 - y0)/(x0 - xorg );

	return scale;
}

void		CSelectedPolygon::ShearMoveY( double x0, double y0, double x1, double y1 )
{
	if( m_bGrouped )
	{
		double scale = GetShearYScale( x0, y0, x1, y1 );

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->ShearY( scale, m_Handles[ POLYHNDL_ANCHOR ].x );
		}
	}
	else
	{
		assert( m_nCurSelectIdx>=0 && m_nCurSelectIdx<Size() );
		CPolygon *pSelPoly = GetPolygon( m_nCurSelectIdx );
		double scale = pSelPoly->GetShearYScale(x0,y0,x1,y1);

		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->ShearY( scale );
		}
	}

	UpdateChanges();
}


//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------
// rotate
double		CSelectedPolygon::GetRotateAngle( double x0, double y0, double x1, double y1 )
{
	CVertex &org = m_Handles[ POLYHNDL_ANCHOR ];
	CVertex v1( x0, y0 );
	CVertex v2( x1, y1 );

	// calculate the rotate angle
	double angle = CGDIUtils::CalcAngle( org, v1, org, v2 );

	return angle;
}

void		CSelectedPolygon::Rotate( double angle )
{
	if( m_bGrouped )
	{
		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->Rotate( angle, m_Handles[ POLYHNDL_ANCHOR ].x, m_Handles[ POLYHNDL_ANCHOR ].y );
		}
	}
	else
	{
		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->Rotate( angle );
		}
	}

	UpdateChanges();
}

void		CSelectedPolygon::RotateMove( double x0, double y0, double x1, double y1 )
{
	double angle;
	if( m_bGrouped )
	{
		angle = GetRotateAngle( x0, y0, x1, y1 );

		//for( int i=0; i<Size(); i++ )
		//{
		//	CPolygon *pPoly = GetPolygon( i );
		//	pPoly->Rotate( angle, m_Handles[ ANCHOR ].x, m_Handles[ ANCHOR ].y );
		//}
	}
	else
	{
		assert( m_nCurSelectIdx>=0 && m_nCurSelectIdx<Size() );
		CPolygon *pSelPoly = GetPolygon( m_nCurSelectIdx );
		angle = pSelPoly->GetRotateAngle(x0,y0,x1,y1);

		//for( int i=0; i<Size(); i++ )
		//{
		//	CPolygon *pPoly = GetPolygon( i );
		//	pPoly->Rotate( angle );
		//}
	}
	Rotate( angle );

	UpdateChanges();
}

// reverse vertex order
void		CSelectedPolygon::ReverseVertexOrder()
{
	for( int i=0; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon( i );
		pPoly->ReverseOrientation();
		pPoly->VertexNormal();
	}
}

// set the first vertex to be anchor: for both grouped and ungrouped objects
void		CSelectedPolygon::SetAnchorAsFirstVertex()
{
	for( int i=0; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon( i );
		pPoly->SetAnchorAsFirstVertex();
	}
}

void		CSelectedPolygon::SetAnchor( double px, double py )
{
	if( m_bGrouped )
	{
		m_Handles[ POLYHNDL_ANCHOR ].Set( px,py );
	}
	else
	{
		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->SetAnchor(px,py);
		}
	}
}

void		CSelectedPolygon::SetAnchorAsCenter()
{
	if( m_bGrouped )
	{
		CVertex pt1 = m_Handles[ POLYHNDL_UPPERLEFT ];
		CVertex pt2 = m_Handles[ POLYHNDL_LOWERRIGHT ];
		SetAnchor( (pt1.x+pt2.x)/2, (pt1.y+pt2.y)/2 );
	}
	else
	{
		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->SetAnchorAsCenter();
		}
	}
}

void		CSelectedPolygon::SetAnchorAsHandle(HandleMode mode)
{
	assert( mode>=0 && mode<POLYHNDL_ANCHOR );
	if( m_bGrouped )
	{
		CVertex pt = m_Handles[ mode ];
		SetAnchor( pt.x, pt.y );
	}
	else
	{
		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->SetAnchorAsHandle(mode);
		}
	}
}

//-----------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------
// flip
void		CSelectedPolygon::FlipHorizontal()
{
	if( m_bGrouped )
	{
		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->FlipHorizontal( m_Handles[ POLYHNDL_ANCHOR ].x );
		}
	}
	else
	{
		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->FlipHorizontal();
		}
	}
}

void		CSelectedPolygon::FlipVertical()
{
	if( m_bGrouped )
	{
		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->FlipVertical( m_Handles[ POLYHNDL_ANCHOR ].y );
		}
	}
	else
	{
		for( int i=0; i<Size(); i++ )
		{
			CPolygon *pPoly = GetPolygon( i );
			pPoly->FlipVertical();
		}
	}
}


// make polygons closed
void		CSelectedPolygon::MakeClosed(bool close)
{
	for( int i=0; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon( i );
		pPoly->SetClosed(close);
	}
}

// Switch polygons closed
void		CSelectedPolygon::SwitchClosed(bool close)
{
	for( int i=0; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon( i );
		if(pPoly->GetClosed())
			pPoly->SetNonClosed();
		else
			pPoly->SetClosed(close);
	}
}

// align the polygons by the top object
void		CSelectedPolygon::AlignLefts()
{
	assert( !m_bGrouped && Size()>1 );
	CPolygon *pTopPoly = this->GetPolygon(0);
	double left = pTopPoly->GetHandle(POLYHNDL_LEFT).x;
	for( int i=1; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		double x = pPoly->GetHandle(POLYHNDL_LEFT).x;
		pPoly->Translate( left-x, 0 );
	}
}

void		CSelectedPolygon::AlignCenters()
{
	assert( !m_bGrouped && Size()>1 );
	CPolygon *pTopPoly = this->GetPolygon(0);
	double center = pTopPoly->GetHandle(POLYHNDL_UPPER).x;
	for( int i=1; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		double x = pPoly->GetHandle(POLYHNDL_UPPER).x;
		pPoly->Translate( center-x, 0 );
	}

}

void		CSelectedPolygon::AlignRights()
{
	assert( !m_bGrouped && Size()>1 );
	CPolygon *pTopPoly = this->GetPolygon(0);
	double right = pTopPoly->GetHandle(POLYHNDL_RIGHT).x;
	for( int i=1; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		double x = pPoly->GetHandle(POLYHNDL_RIGHT).x;
		pPoly->Translate( right-x, 0 );
	}
}

void		CSelectedPolygon::AlignTops()
{
	assert( !m_bGrouped && Size()>1 );
	CPolygon *pTopPoly = this->GetPolygon(0);
	double top = pTopPoly->GetHandle(POLYHNDL_UPPER).y;
	for( int i=1; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		double y = pPoly->GetHandle(POLYHNDL_UPPER).y;
		pPoly->Translate( 0, top-y );
	}
}

void		CSelectedPolygon::AlignMiddles()
{
	assert( !m_bGrouped && Size()>1 );
	CPolygon *pTopPoly = this->GetPolygon(0);
	double middle = pTopPoly->GetHandle(POLYHNDL_LEFT).y;
	for( int i=1; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		double y = pPoly->GetHandle(POLYHNDL_LEFT).y;
		pPoly->Translate( 0, middle-y );
	}
}

void		CSelectedPolygon::AlignBottoms()
{
	assert( !m_bGrouped && Size()>1 );
	CPolygon *pTopPoly = this->GetPolygon(0);
	double bottom = pTopPoly->GetHandle(POLYHNDL_BOTTOM).y;
	for( int i=1; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		double y = pPoly->GetHandle(POLYHNDL_BOTTOM).y;
		pPoly->Translate( 0, bottom-y );
	}
}

void		CSelectedPolygon::AlignAnchors()
{
	assert( !m_bGrouped && Size()>1 );
	CPolygon *pTopPoly = this->GetPolygon(0);
	double anx = pTopPoly->GetHandle(POLYHNDL_ANCHOR).x;
	double any = pTopPoly->GetHandle(POLYHNDL_ANCHOR).y;
	for( int i=1; i<Size(); i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		double x = pPoly->GetHandle(POLYHNDL_ANCHOR).x;
		double y = pPoly->GetHandle(POLYHNDL_ANCHOR).y;
		pPoly->Translate( anx-x, any-y );
	}


//#define PI 3.141592653589793238462643383279502884197169399375105820974944592308
//
//	//## added by Ligang Liu, testing calibration for two polygons
//	assert( !m_bGrouped && Size() == 2 );
//	CPolygon *pPoly1 = this->GetPolygon(0);
//	CPolygon *pPoly2 = this->GetPolygon(1);
//
//	// we just calibrate two polygons, they should have same number of vertices
//	int num = pPoly1->NumVertex();
//	assert( num == pPoly2->NumVertex() );	
//
//	CVertex center = pPoly2->GetHandle( ANCHOR );
//
//	double anglesum = 0, anglesumabs = 0;
//	double anglesumpos=0, anglesumneg=0;
//
//	double rotangle = 10;
//	while( fabs(rotangle) > 0.001 )
//	{
//		anglesum = anglesumabs = 0;
//		anglesumpos = anglesumneg = 0;
//
//		// calculate the total sum of the angle of all corresponding edges
//		for( int i=0; i< num; i++ )
//		{
//			CVertex *srcV0 = pPoly1->GetVertex(i);
//			CVertex *srcV1 = pPoly1->GetVertex((i+1)%num);
//			CVertex *dstV0 = pPoly2->GetVertex(i);
//			CVertex *dstV1 = pPoly2->GetVertex((i+1)%num);
//
//			double angle = CGDIUtils::CalcAngle( *srcV0, *srcV1, *dstV0, *dstV1 );
//			//double angle = CGDIUtils::CalcAngle( *srcV1, *srcV0, *dstV0, *dstV1 );
//
//			if( angle >= 0 )
//				anglesumpos += angle;
//			else
//				anglesumneg += angle;
//			anglesum += angle;
//			anglesumabs += fabs( angle );
//		}
//
//		rotangle = anglesum / (double)num;
//
//		// rotate the second polygon by angle "-rotangle"
//		pPoly2->Rotate( -rotangle, center.x, center.y );
//	}
//	// ## e.o.m. by Ligang Liu, 04/06/17
}


void		CSelectedPolygon::AlignDistributeHorizontally()
{
	int num = Size();
	assert( !m_bGrouped && num>2 );
	double x0 = GetPolygon(0)->GetHandle(POLYHNDL_UPPER).x;
	double x1 = GetPolygon(num-1)->GetHandle(POLYHNDL_UPPER).x;
	double delta = (x1-x0)/(num-1);
	for( int i=1; i<num-1; i++ )
	{
		double xnew = x0 + delta * i;
		CPolygon *pPoly = GetPolygon(i);
		double x = pPoly->GetHandle(POLYHNDL_UPPER).x;
		pPoly->Translate( xnew-x, 0 );
	}
}


void		CSelectedPolygon::AlignDistributeVertically()
{
	int num = Size();
	assert( !m_bGrouped && Size()>2 );
	double y0 = GetPolygon(0)->GetHandle(POLYHNDL_LEFT).y;
	double y1 = GetPolygon(num-1)->GetHandle(POLYHNDL_LEFT).y;
	double delta = (y1-y0)/(num-1);
	for( int i=1; i<num-1; i++ )
	{
		double ynew = y0 + delta * i;
		CPolygon *pPoly = GetPolygon(i);
		double y = pPoly->GetHandle(POLYHNDL_LEFT).y;
		pPoly->Translate( 0, ynew-y );
	}
}

void		CSelectedPolygon::AlignOnTopObject(BOOL bAlignTangent)
{
	int num = Size();
	assert( !m_bGrouped && Size()>1 );

	CPolygon *pTopPoly = GetPolygon(0);		// the top polygon
	int nNumTopPoly = pTopPoly->NumVertex();

	// calculate the align path, this can be done by other methods
	std::vector<CVertex*>  AlignPath;
	for( int i=1; i<num; i++ )
	{
		AlignPath.push_back( pTopPoly->GetVertex((i-1)%nNumTopPoly) );
	}

	// calculate the rotate angles
	std::vector<double> AnglePath;
	if( bAlignTangent )
	{
		for( int i=0; i<num-1; i++ )
		{
			CVertex *pV0, *pV1;
			if( i==0 )
				pV0 = AlignPath[0];
			else
				pV0 = AlignPath[i-1];
			if( i==num-2 )
				pV1 = AlignPath[num-2];
			else
				pV1 = AlignPath[i+1];

			AnglePath.push_back( CGDIUtils::CalcAngle(CVertex(0,0),CVertex(1,0), *pV0, *pV1 ) );
		}
	}

	// align other objects by the path
	for( int i=1; i<num; i++ )
	{
		CPolygon *pPoly = GetPolygon(i);
		CVertex anchor = pPoly->GetHandle(POLYHNDL_ANCHOR);
		pPoly->Translate( AlignPath[i-1]->x - anchor.x, AlignPath[i-1]->y - anchor.y );
		if( bAlignTangent )
			pPoly->Rotate( AnglePath[i-1] );
	}
}


// void		CSelectedPolygon::DelaunayTriangulation()
// {
// 	for( int i=0; i<this->Size(); i++ )
// 	{
// 		CPolygon *pPoly = GetPolygon(i);
// 		pPoly->SetClosed(true);
// 		pPoly->DelaunayTriangulation("Triangulation");
// 	}
// }
